package CSS::Parser::AsBuilder::Styles;
use Moose;
use Carp;

use lib '../../../../lib';
use CSS::Builder::Style;
use CSS::Builder::Style::Value::Str;
use CSS::Builder::Style::Value::Set;
use CSS::Builder::Style::Value::Num;
use CSS::Builder::Style::Value::Url;
use CSS::Builder::Style::Value::Unit;
use CSS::Builder::Style::Value::Color;
use CSS::Builder::Style::Value::Inherit;
use CSS::Builder::Style::Value::Percent;

our $COLOR_REGEX = '(#\w{1,6}|rgb\(.+\)|aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgrey|darkgreen|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|grey|green|greenyellow|honeydew|hotpink|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgrey|lightgreen|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen|transparent)';
our $STYLE_REGEX = '(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)';
our $UNITS_REGEX = '\s?([0-9.]+\s?(em|ex|px|in|cm|mm|pt|pc|%)?)';

sub build_styles {
      my ($self , $contents) = @_;
      return $self->parse_styles($contents);
}

sub parse_styles {
      my ($self , $contents) = @_;
      
      my @res = ();
      # parse the styles itself
      foreach ( grep { /\S/ } split /\;/, $contents ) {
	    unless ( /^\s*([\w._-]+)\s*:\s*(.*?)\s*$/ ) {
		  croak( "Invalid or unexpected property '$_' in style '$contents'" );
	    }
            
            push @res , $self->create_new_style($1 , $2);
      }
      return \@res;
}

sub process_styles { # <- DEPRICATED
      my ($self , $names , $values) = @_;
      
      my @res = map { $self->create_new_style($_ , $values->{$_}) } @$names;
      return \@res;
}

sub create_new_style {
      my ($self , $name , $value) = @_;
      
      my $dashes_rm = $name;
      $dashes_rm =~ s/-/_/g;
      
      my @complex = qw(
            background
            border border_top border_left border_bottom border_right
            cue font list_style margin outline padding
      );
      
      my $style = CSS::Builder::Style->new();
      $style->name($name);
      
      if ($self->check_important($value)) {
            $style->important(1);
            $value = $self->clear_important($value);
      }
      
      if ($self->check_inherit($value)) {
            $style->value($self->new_inherit);
      } else {
            if (grep { $_ eq $dashes_rm} @complex) {
                  my $parse = 'parse_' . $dashes_rm . '_element';
                  $style->push($self->$parse($value));
            } else {
                  my $method = 'new_' . $self->define_type($value);
                  $style->value($self->$method($value));
            }
      }
      
      return $style;
}

# vvv subject to change vvv

sub define_type {
      my ($self , $value) = @_;
      
      my @map = (
            set         => '^[0-9a-zA-Z%.-]+\s+[0-9a-zA-Z%.-]+',
            inherit     => '^inherit$',
            color       => '^#|rgb|aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgrey|darkgreen|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|grey|green|greenyellow|honeydew|hotpink|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgrey|lightgreen|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen',
            str         => '^[a-zA-Z ,\'"-]+$',
            num         => '^(\+|-)?[0-9.]+$',
            percent     => '^(\+|-)?[0-9.]+%$',
            unit        => '^(\+|-)?[0-9.]+(em|ex|px|in|cm|mm|pt|pc)$',
            url         => '^url',
      );
      
      for (my $i = 0; $i <= $#map; $i += 2) {
            return $map[$i] if lc($value) =~ /$map[$i + 1]/;
      }
      
      # return str so that we don't get a error
      return 'str';
      
      # if we got here, we are in trouble
      croak "Can't determine type of '$value'\n";
}

### TYPES ###

sub new_set {
      my ($self , $value) = @_;
      
      my $set = CSS::Builder::Style::Value::Set->new(
            value => $value
      );
      
      return $set;
}

sub new_color {
      my ($self , $value) = @_;
      
      my $color = CSS::Builder::Style::Value::Color->new(
            value => $value
      );
      
      return $color;
}

sub new_inherit {
      my $self = shift;
      
      my $inherit = CSS::Builder::Style::Value::Inherit->new(
            inherit => 1
      );
      
      return $inherit;
}

sub new_str {
      my ($self , $value) = @_;
      
      my $str = CSS::Builder::Style::Value::Str->new(
            value => $value
      );
      
      return $str;
}

sub new_num {
      my ($self , $value) = @_;
      
      my $num = CSS::Builder::Style::Value::Num->new();
      
      $num->positive(0) if $value =~ /^-/;
      
      $value =~ /(\+|-)?([0-9.]+)/;
      $num->value($2);
      
      return $num;
}

sub new_unit {
      my ($self , $value) = @_;
      
      
      my $unit = CSS::Builder::Style::Value::Unit->new();
      
      $unit->positive(0) if $value =~ /^-/;
      
      $value =~ /([0-9.]+)(\s+)?(em|ex|px|in|cm|mm|pt|pc)/;
      $unit->value($1);
      $unit->unit($3);
      
      return $unit;
}

sub new_percent {
      my ($self , $value) = @_;
      
      my $percent = CSS::Builder::Style::Value::Percent->new();
      
      $percent->positive(0) if $value =~ /^-/;
      
      $value =~ /(\+|-)?([0-9.]+)%/;
      $percent->value($2);
      
      return $percent;
}

sub new_url {
      my ($self , $value) = @_;
      
      my $url = CSS::Builder::Style::Value::Url->new(
            value => $value
      );
      
      return $url;
}

###############################

### UTILS ###

sub create_se {
      my ($self , $name , $value) = @_;
      
      $value =~ s/^\s+//;
      $value =~ s/\s+$//;
      $value =~ s/\s+/ /g;
      
      my $style = CSS::Builder::Style->new(name => $name);
      
      my $method = 'new_' . $self->define_type($value);
      $style->value($self->$method($value));
      
      return $style;
}

sub check_important {
      my ($self , $string) = @_;
      
      return 1 if ($string =~ /!important/);
}

sub check_inherit {
      my ($self , $string) = @_;
      
      return 1 if ($string =~ /inherit/);
}

sub clear_important {
      my ($self , $string) = @_;
      
      $string =~ s/(\s+)?!important//;
      
      return $string;
}

###############################

### PARSE COMPLEX STYLES ###

sub parse_list_style_element {
      my ($self , $value) = @_;
      
      my @res = ();    
      my @map = (
            'list-style-type'       => '(disc|circle|square|decimal|decimal-leading-zero|lower-roman|upper-roman|lower-greek|lower-alpha|lower-latin|upper-alpha|upper-latin|hebrew|armenian|georgian|cjk-ideographic|hiragana|katakana|hiragana-iroha|katakana-iroha|none)',
            'list-style-position'   => '(inside|outside)',
            'list-style-image'      => '((url(\s+)?\((\s+)?.+(\s+)?\))|none)'
      );
      
      for (my $i = 0; $i <= $#map; $i += 2) {
            push @res , $self->create_se($map[$i] , $1) if $value =~ s/$map[$i + 1]//;
      }
      
      return @res;
}

sub parse_margin_element {
      my ($self , $value) = @_;
      
      my @res = ();
      $value =~ s/\s+/ /g;
      my @val = split / / , $value;
      
      if ($#val eq 0) {
            push @res , $self->create_se('margin-top' , $val[0]);
            push @res , $self->create_se('margin-right' , $val[0]);
            push @res , $self->create_se('margin-bottom' , $val[0]);
            push @res , $self->create_se('margin-left' , $val[0]);
      } elsif ($#val eq 1) {
            push @res , $self->create_se('margin-top' , $val[0]);
            push @res , $self->create_se('margin-right' , $val[1]);
            push @res , $self->create_se('margin-bottom' , $val[0]);
            push @res , $self->create_se('margin-left' , $val[1]);
      } elsif ($#val eq 2) {
            push @res , $self->create_se('margin-top' , $val[0]);
            push @res , $self->create_se('margin-right' , $val[1]);
            push @res , $self->create_se('margin-bottom' , $val[2]);
            push @res , $self->create_se('margin-left' , $val[1]);
      } elsif ($#val eq 3) {
            push @res , $self->create_se('margin-top' , $val[0]);
            push @res , $self->create_se('margin-right' , $val[1]);
            push @res , $self->create_se('margin-bottom' , $val[2]);
            push @res , $self->create_se('margin-left' , $val[3]);
      }
      
      return @res;
}

sub parse_padding_element {
      my ($self , $value) = @_;
      
      my @res = ();
      $value =~ s/\s+/ /g;
      my @val = split / / , $value;
      
      if ($#val eq 0) {
            push @res , $self->create_se('padding-top' , $val[0]);
            push @res , $self->create_se('padding-right' , $val[0]);
            push @res , $self->create_se('padding-bottom' , $val[0]);
            push @res , $self->create_se('padding-left' , $val[0]);
      } elsif ($#val eq 1) {
            push @res , $self->create_se('padding-top' , $val[0]);
            push @res , $self->create_se('padding-right' , $val[1]);
            push @res , $self->create_se('padding-bottom' , $val[0]);
            push @res , $self->create_se('padding-left' , $val[1]);
      } elsif ($#val eq 2) {
            push @res , $self->create_se('padding-top' , $val[0]);
            push @res , $self->create_se('padding-right' , $val[1]);
            push @res , $self->create_se('padding-bottom' , $val[2]);
            push @res , $self->create_se('padding-left' , $val[1]);
      } elsif ($#val eq 3) {
            push @res , $self->create_se('padding-top' , $val[0]);
            push @res , $self->create_se('padding-right' , $val[1]);
            push @res , $self->create_se('padding-bottom' , $val[2]);
            push @res , $self->create_se('padding-left' , $val[3]);
      }
      
      return @res;
}

sub parse_outline_element {
      my ($self , $value) = @_;
      
      my @res = ();
      my @map = (
            'outline-color' => $COLOR_REGEX,
            'outline-style' => '(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)',
            'outline-width' => '(thin|medium|thick)',
      );
      
      for (my $i = 0; $i <= $#map; $i += 2) {
            push @res , $self->create_se($map[$i] , $1) if $value =~ s/$map[$i + 1]//;
      }
      
      return @res;
}

sub parse_font_element {
      my ($self , $value) = @_;
      
      my @res = ();
      my @map = (
            'font-style'      => '(normal|italic|oblique)',
            'font-variant'    => '(normal|small-caps)',
            'font-weight'     => '(normal|bold|bolder|lighter|[1-9]00)',
            'font-size'       => '((\+|-)?([0-9.]+)(em|ex|px|in|cm|mm|pt|pc|%)?)',
            'line-height'     => '\/((\+|-)?([0-9.]+)(em|ex|px|in|cm|mm|pt|pc|%)?)'
      );
      
      for (my $i = 0; $i <= $#map; $i += 2) {
            push @res , $self->create_se($map[$i] , $1) if $value =~ s/$map[$i + 1]//;
      }
      
      if ($value =~ /\w+/) {
            push @res , $self->create_se('font-family' , $value);
      }
      
      return @res;
} 

sub parse_cue_element {
      my ($self , $value) = @_;
      my @res = ();
      return @res;
}


sub parse_background_element {
      my ($self , $value) = @_;

      my @res = ();
      my @map = (
            'background-color'      => $COLOR_REGEX,
            'background-image'      => '(url(\s+)?\((\s+)?.+(\s+)?\)|none)',
            'background-repeat'     => '(repeat-x|repeat-y|no-repeat|repeat)',
            'background-attachment' => '(scroll|fixed)',
            'background-position'
                  => '((((\+|-)?[0-9.]+(\s+)?(em|ex|px|in|cm|mm|pt|pc|%)?)|(top|center|bottom))\s+(((\+|-)?[0-9.]+(\s+)?(em|ex|px|in|cm|mm|pt|pc|%)?)|(left|center|right)))',
      );
      
      for (my $i = 0; $i <= $#map; $i += 2) {
            push @res , $self->create_se($map[$i] , $1) if $value =~ s/$map[$i + 1]//;
      }
      
      return @res;
}

sub parse_border_element {
      my ($self , $value) = @_;
      
      my @res = ();
      
      my $s = $self->_parse_border($value);
      
      push @res , $self->create_se('border-width' , $s->{width}) if (defined $s->{width}); 
      push @res , $self->create_se('border-style' , $s->{style}) if (defined $s->{style}); 
      push @res , $self->create_se('border-color' , $s->{color}) if (defined $s->{color});
      
      return @res;
}

sub parse_border_top_element {
      my ($self , $value) = @_;
      
      my @res = ();
      
      my $s = $self->_parse_border($value);
      
      push @res , $self->create_se('border-top-width' , $s->{width}) if (defined $s->{width}); 
      push @res , $self->create_se('border-top-style' , $s->{style}) if (defined $s->{style}); 
      push @res , $self->create_se('border-top-color' , $s->{color}) if (defined $s->{color});
      
      return @res;
}

sub parse_border_right_element {
      my ($self , $value) = @_;
      
      my @res = ();
      
      my $s = $self->_parse_border($value);
      
      push @res , $self->create_se('border-right-width' , $s->{width}) if (defined $s->{width}); 
      push @res , $self->create_se('border-right-style' , $s->{style}) if (defined $s->{style}); 
      push @res , $self->create_se('border-right-color' , $s->{color}) if (defined $s->{color});
      
      return @res;
}

sub parse_border_bottom_element {
      my ($self , $value) = @_;
      
      my @res = ();
      
      my $s = $self->_parse_border($value);
      
      push @res , $self->create_se('border-bottom-width' , $s->{width}) if (defined $s->{width}); 
      push @res , $self->create_se('border-bottom-style' , $s->{style}) if (defined $s->{style}); 
      push @res , $self->create_se('border-bottom-color' , $s->{color}) if (defined $s->{color});
      
      return @res;
}

sub parse_border_left_element {
      my ($self , $value) = @_;
      
      my @res = ();
      
      my $s = $self->_parse_border($value);
      
      push @res , $self->create_se('border-left-width' , $s->{width}) if (defined $s->{width}); 
      push @res , $self->create_se('border-left-style' , $s->{style}) if (defined $s->{style}); 
      push @res , $self->create_se('border-left-color' , $s->{color}) if (defined $s->{color});
      
      return @res;
}

sub _parse_border {
      my ($self , $value) = @_;
      
      my %res = ();
      
      $res{color} = $1 if $value =~ s/$COLOR_REGEX//;
      $res{width} = $1 if $value =~ s/$UNITS_REGEX//;
      $res{style} = $1 if $value =~ s/$STYLE_REGEX//;
      
      return \%res;
}

###############################

1;